
package tankattack.world;

import gamepack.*;
import tankattack.sprites.*;
import java.awt.event.*;
import java.awt.*;
import java.util.Vector;
import java.util.Enumeration;

/** @author Somanath

Requirements:
----------------------------------------------------------------------
Enemy Bullets don't hurt EnemyBullets and EnemyTanks
Player1's Bullets stun Player2 and vica versa.
A player bullet does not stun itself.
If any 2 bullets collide(except if they are of the same spawner), they die.


Sprites/Attributes Schema for Tank Attack -
-------------------------------------------------------------------------------|
Sprite/type    Attributes       Blockers   Colliders                           | Runtime Class Dependencies         
-------------------------------------------------------------------------------|
Tank  P1    |  BLOCK            | BLOCK |**ETank,SOLIDBRICK,P2BULLET,Tank      | spawns Bullet|kills EnemyTank,Bullets|hurts Bricks
      P2    |  BLOCK            | BLOCK |**ETank,SOLIDBRICK,P1BULLET,Tank      | spawns Bullet|kills EnemyTank,Bullets|hurts Bricks
ETank       |  BLOCK            | BLOCK |**Tank,SOLIDBRICK,P1BULLET,P2BULLET   | spawns Bullet|kills Tank,Bullets|hurts Bricks
Bullet P1   |  P1_BULLET        |   -   |ETank,SOLIDBRICK,P2BULLET,EBULLET,Tank| spawns 'Explosion' Object
       P2   |  P2_BULLET        |   -   |ETank,SOLIDBRICK,P1BULLET,EBULLET,Tank|
       ENEMY|  ENEMY_BULLET     |   -   |Tank,SOLIDBRICK,P1BULLET,P2BULLET     | 
FlexiBrick  | BLOCK             |   -   |                                      | NA
Brick       |         -         |   -   |                                      | NA
   WEAK     | BLOCK,SOLIDBRICK  |   -   |                                      |
   STRONG   | BLOCK,SOLIDBRICK  |   -   |                                      |
   BASE     | BLOCK,SOLIDBRICK  |   -   |                                      |
   FOREST   |                   |   -   |                                      |
   WATER    | BLOCK             |   -   |                                      |
Explosion   |         -         |   -   |                                      | NA
   BIG      |         -         |   -   |                                      |
   MEDIUM   |         -         |   -   |                                      |
   SMALL    |         -         |   -   |                                      |
-------------------------------------------------------------------------------|
                                                                                *
Global Attributes to be made a part of gamepack - 
BLOCK - Any sprite which can block other sprites.																				
HURTABLE - Any sprite that can be hurt/killed by bullets|weapons etc.																		 - 

Simplistic Approach (doesn't take care of exact Requirements ??  ====

Enemy Bullets don't hurt EnemyBullets and EnemyTanks
Player1's Bullets stun Player2 and vica versa.
A player bullet does not stun itself.
If any 2 bullets collide(except if they are of the same spawner), they die.


Sprites/Attributes Schema for Tank Attack -
-------------------------------------------------------------------------------|
Sprite/type    Attributes       Blockers   Colliders                           | spawns|  InteractsWith  
-------------------------------------------------------------------------------|       |
Tank  P1|P2 |  BLOCK|HURTABLE   | BLOCK | **HURTABLE                           | Bullet| hurts *Hurtable (except spawner's kind & kind's bullet)
EnemyTank   |  BLOCK|HURTABLE   | BLOCK | **HURTABLE                           | Bullet| hurts *Hurtable (except spawner's kind & kind's bullet)
Bullet      |                   |   -   | (based on Spawner see above)         | Explosion | (think of Bullets as extension of their creator)
FlexiBrick  |  BLOCK            |   -   |                                      | NA
Brick       |         -         |   -   |                                      | NA
   WEAK     | BLOCK,HURTABLE    |   -   |                                      |
   STRONG   | BLOCK,HURTABLE    |   -   |                                      |
   BASE     | BLOCK,HURTABLE    |   -   |                                      |
   FOREST   |                   |   -   |                                      |
   WATER    | BLOCK             |   -   |                                      |
Explosion   |         -         |   -   |                                      | NA
   BIG      |         -         |   -   |                                      |
   MEDIUM   |         -         |   -   |                                      |
   SMALL    |         -         |   -   |                                      |
-------------------------------------------------------------------------------|
                                                                                *
Global Attributes to be made a part of gamepack - 
BLOCK - Any sprite which can block other sprites.																				
HURTABLE - Any sprite that can be hurt/killed by bullets|weapons etc.																				
                                                                                **/

																				
public class TankAttackWorld extends World implements Traversable, NotificationHandler {

// STUFF not strictly requried for RoomA and RoomB ------------------------
	public static final int	ROOM_STARTUP=0;
	//static String[] midiFiles = { "roomStartUp.mid" };
	//static Music[] _midi;
	static final int NO_FINISH=0,GOOD_FINISH=1,BAD_FINISH=2;
	static MyMouseListener ml = new MyMouseListener();
	//static Image pointer;
	static int px=232,py=238,ph=26;				// pointer position
	static int pos=0;
	static Rectangle[] r = new Rectangle[3];	// 3 rectangular regions.
	static boolean is2PGame = false;
	static Font scoreFont = new Font("Serif",Font.BOLD,20);
	
	
	static Resource res = ResourceLoader.getResource("tankattack.world.TankAttackWorld");
	static Image gameOverImage = res.img[0][0].img;	//ResourceLoader.getImage("gameover.gif");
	
	
	
	
	// Stuff that is read from configuration file --------------------------------
	
	static TimeStamp enemySpawnTime = new TimeStamp(1000);	
	static TimeStamp scoreDelay = new TimeStamp(2000);
	static RoomData rd;  
	static int player1Life = 3;
	static int player2Life = 3;
    static Sprite base;
    static Sprite player1;
    static Sprite player2;
	static String nextRoom;
	static String resetRoom;
	static int maxEnemies;
	
	// Other stuff that gets changed and needs to be initialized -------------------------------
	static Rectangle[] playerSpawnArea = new Rectangle[2];
	static Sprite[] dummyEnemyTank  = new Sprite[3];
	
	
	static int goiPosX = 180;
	static int goiPosY = 400;	// "GameOver" position
	static boolean playersDead = false;
	static int roomFinish = NO_FINISH;
	static int numEnemiesSpawned = 0;
	static int index = 0;  				// index of enemyPosition[]
	static int[] p1Score = new int[2];  // Score of p1 against type1 enemy and type2 enemy
	static int[] p2Score = new int[2];  // Score of p2 against type1 enemy and type2 enemy


	public void handleNotification(Notification n) {
		// Contract - 
		// n.type tells who killed it P1 or P2
		// n.intArr[0] is assumed to give the 'type' of the sprite killed. (weak or strong)
		// n.strArr[0] is assumed to give the kind of sprite killed (EnemyTank)
	
		if( n.type.equals("P1_KILLED_SPRITE") ) {      // P1 has killed something
			if ( n.strArr != null && n.strArr.length > 1 && n.strArr[0].equals("EnemyTank") ) { // If that something is EnemyTank
				p1Score[ n.intArr[0] ]++;		// Update score // n.intArr[0] is assumed to give the 'type' of the sprite killed.
			}
		} else if ( n.type.equals("P2_KILLED_SPRITE") ) {  // P2 has killed something
			if ( n.strArr != null && n.strArr.length > 1 && n.strArr[0].equals("EnemyTank") ) { // If that something is EnemyTank
				p2Score[ n.intArr[0] ]++;		// Update score
			}
		}
	}

		public static void initVars() {
			goiPosX = 180;	// "GameOver" position 
			goiPosY = 400;
			playersDead = false;
			roomFinish = NO_FINISH;
			numEnemiesSpawned = 0;
			index = 0;
			scoreDelay.reset();
			resetScores();
		}
		
		public static void resetScores() {
			for(int i=0;i<2;i++)  p1Score[i] = p2Score[i] = 0;
		}		
		
		Point[] enemyPosition = new Point[]
		{	new Point(50,50),	new Point(300,50),	new Point(550,50)	};

		Point[] playerPosition = new Point[]
		{	new Point(500,760),	new Point(700,760)  };

    public void updateGame() {
           Sprite.update();
/*      for( Enumeration e= Sprite.allSprites.elements(); e.hasMoreElements(); ) {
           Sprite s = (Sprite) e.nextElement();
           s.run();
      }*/
    }

	public TankAttackWorld() {
		//roomImg = ResourceLoader.getImage(backgrounds);
		//pointer = ResourceLoader.getImage("pointer.gif");
		r[0] = new Rectangle(px,py+ph*0,140,ph);
		r[1] = new Rectangle(px,py+ph*1,140,ph);
		r[2] = new Rectangle(px,py+ph*2,140,ph);

		//MusicEngine.reset();
		///midi = ResourceLoader.getMidi(midiFiles);
	//	_midi = ResourceLoader.getMusic(midiFiles);
		//Sprite.getFamily( new String[] {  "Tank", "Brick", "EnemyTank", "Explosion", "FlexiBrick" } );
		String[] classNames = { "tankattack.sprites.Brick", "tankattack.sprites.EnemyTank", "tankattack.sprites.Tank",
                            "tankattack.sprites.Explosion", "tankattack.sprites.FlexiBrick" , "tankattack.sprites.Bullet" };

		//Vector[] touch = { Brick.family, Tank.family, EnemyTank.family, Explosion.family, FlexiBrick.family };
		//int[] touch = { Brick.DEAD, Tank.DEAD, EnemyTank.DEAD, Explosion.DEAD, FlexiBrick.DEAD, Bullet.DEAD };
		System.out.println("Inside TankAttack() ctr start ----" );
		Environment.touchFamilies( classNames ); // Load these classes ahead of their time.
		System.out.println("Inside TankAttack() ctr start ---");

		startWorld();
	}

    public Object newInstance(Class c) throws InstantiationException, IllegalAccessException  {
            return c.newInstance();
    }



	public void run() {
		Application.setKeyListener( new Controls() );
		 setNextRoom( "RoomStartUp" );
			//setSleepTime(20);
		Game.verbose = Game.VERBOSE_PRINT + Game.VERBOSE_GENERAL;
		while ( worldIsRunning() ) { // The point where game can get paused
			curRoom.run();
			if( nextRoomSet() )     // Any Sprite or Room may request a room change.
			   changeToNextRoom();  // Room change logic

		}
		curRoom.exit(); // cleanup room if any..
		Application.setKeyListener(null);
	}

	/*public void spawnBricks(int x,int y,int w,int h,int type) {
		for(int j=0;j<h;j++)
			for(int i=0;i<w;i++)
				//new Brick(x+i*Brick.width+1,y+j*Brick.height+1,type);
				Sprite.spawnClass("tankattack.sprites.Brick","x="+(x+i*Brick.width+1)+",y="+(y+j*Brick.height+1)+",type="+(type));
	}*/

	public void displayLives(Graphics g) {
		g.setColor(Color.black);
		g.setFont(new Font("Serif",Font.BOLD,20));
		g.drawString("" + player1Life,10,28);
		if( is2PGame )
			g.drawString("" + player2Life,580,28);
		/*g.drawString("allSprites.size()=" + Sprite.allSprites.size() ,100,100);
		g.drawString("players.size()=" + Sprite.players.size() ,100,120);
		g.drawString("passive.size()=" + Sprite.passive.size() ,100,140);*/
	}

	/*String attrstr(int attr ) {
		StringBuffer b = new StringBuffer();
		for(int i=8;i>=0;i--) {
			if( (attr & (1<<i)) == (1<<i) )	b.append("1");
			else b.append("0");
			if(i == 4 ) b.append(" ");
		}
		return b.toString();
	}*/

	public void displayScores(Graphics g) {
		int sx = 80,sy = 100;
		g.setColor(Color.blue);
		g.fillRect(0,0,600,400);
		g.setFont(new Font("Serif",Font.BOLD,20));
		g.setColor(Color.black);

		g.drawString(" Player1: Enemy1 x "  + p1Score[0] + "  Enemy2 x " +  p1Score[1] +
			" Total = " + (p1Score[0] +p1Score[1]) ,sx,sy );
		if( is2PGame )
			g.drawString(" Player2: Enemy1 x "  + p2Score[0] + "  Enemy2 x " +  p2Score[1] +
				" Total = " + (p2Score[0] + p2Score[1]) ,sx,sy + 40 );

		g.drawString(" Press Enter to Continue ",sx,sy+80);
	}

	public void displayGameOver(Graphics g) {
		if( goiPosY > 100 ) goiPosY-=4;
		g.drawImage(gameOverImage,goiPosX,goiPosY,null);

	}

	public boolean canSpawnPlayer(int thePlayer) {
		Rectangle r = playerSpawnArea[thePlayer];
						//Tank.r.img[thePlayer][Tank.UP].rect;
		//int x = playerPosition[thePlayer].x + r.x;
		//int y = playerPosition[thePlayer].y + r.y;
		
		SpriteArgs sa = SpriteArgs.create( TankAttackWorld.rd.spawnStrings[thePlayer] ,null);
		
		int x = sa.x + r.x;
		int y = sa.y + r.y;
		int w = r.width;
		int h = r.height;
		Collidable c = Collision.createCollidable( new Rectangle( x,y,w,h) );
		Vector<Collidable> vs = (Vector<Collidable>) (Vector)Environment.getAttribute("BLOCK");
		Vector<Collidable> vc = Collision.getCollisions(c,vs);
		if( vc.size() != 0)
		{ return false; }
		else  { return true; }

	}

	public boolean canSpawnEnemy() {  // enemySpawnArea
		Rectangle r = dummyEnemyTank[index].getBounds();
		//EnemyTank.r.img[0][EnemyTank.DOWN].rect;
		/*
		SpriteArgs sa = SpriteArgs.create( TankAttackWorld.rd.spawnStrings[3+index] ,null); // 2,3,4th SpawnString
		
		int x = sa.x + r.x;
		int y = sa.y + r.y;
		int w = r.width;
		int h = r.height;*/
		//Collidable c = Collision.createCollidable( new Rectangle( x,y,w,h) );
		Collidable c = Collision.createCollidable( r );
		
		Vector<Collidable> vc = Collision.getCollisions(c,(Vector)Environment.getAttribute("BLOCK"));
		boolean retVal =  (vc.size() == 0) ? true : false;
		
		return retVal;
	}

	public static void doChoice() {
		switch(pos) {
			case 0: is2PGame = false;	break;
			case 1: is2PGame = true;	break;
			case 2: System.exit(0);
		}
		World.currentWorld.setNextRoom( "RoomA" );
	}

	public boolean traversing(int x,int y,int dx,int dy,int pos,int maxpos) {
		Game.gback.drawLine(x,y,x,y);
		if( pos + 50 > maxpos ) return false;
		return true;
	}
	
	public static void updateCurRoom() {
       TankAttackWorld CG = (TankAttackWorld) World.currentWorld;
			//display();
			if( CG.roomFinish != CG.NO_FINISH) {
				if( CG.roomFinish == CG.GOOD_FINISH ) {
					if( CG.scoreDelay.elapsed() ) { // delay occurs only the first time.
					  if( Application.key[KeyEvent.VK_ENTER] )
							CG.setNextRoom( CG.nextRoom ); //Room.getDefaultRoom() );
					   CG.scoreDelay.set();
					}
				}
				else if(CG.roomFinish == CG.BAD_FINISH) { // ok game over
					if( Application.key[KeyEvent.VK_ENTER] )
						CG.setNextRoom( CG.resetRoom );
				}
			}
			else { // game is going on
				// --------------------------------------------
				// if time to spawn enemies
				if( CG.enemySpawnTime.elapsed() && CG.numEnemiesSpawned < CG.maxEnemies) {
					if( CG.canSpawnEnemy() ) {
						//int num = (int) Math.abs((Math.random()*1)+ 0.5);
						int num = CG.index % 2;
		Sprite.spawnClass("tankattack.sprites.EnemyTank","x="+(CG.dummyEnemyTank[CG.index].x)+",y="+(CG.dummyEnemyTank[CG.index].y)+",type="+num);
						CG.index = CG.index<2 ? CG.index+1 : 0;
						CG.numEnemiesSpawned++;
					}
					else CG.enemySpawnTime.set();
				}

				// if time to spawn player1
				if( !player1.valid() && CG.player1Life >= 0 && CG.canSpawnPlayer(Tank.PLAYER1) ) {
					if( CG.player1Life == 0 ) playersDead = true;
					else {
						//String s = "x="+(CG.playerPosition[0].x)+",y="+(CG.playerPosition[0].y)+",type="+(Tank.PLAYER1)+",dir="+(Tank.UP);
						CG.player1 = Sprite.spawn( rd.spawnStrings[0],null );
						//vp.s = player1;
						World.currentWorld.curRoom.vp.s = CG.player1;
						CG.player1Life--;
					}
				}

				// if time to spawn player2
				if( TankAttackWorld.is2PGame && !player2.valid() && CG.player2Life >= 0 && CG.canSpawnPlayer(Tank.PLAYER2) ) {
					if( CG.player2Life == 0 ) playersDead = true;
					else {
						//String s = "x="+(CG.playerPosition[1].x)+",y="+(CG.playerPosition[1].y)+",type="+(Tank.PLAYER2)+",dir="+(Tank.UP);
						player2 = Sprite.spawn( rd.spawnStrings[1],null);
						CG.player2Life--;
					}
				}
				// if all enemies killed
				if( CG.numEnemiesSpawned >= CG.maxEnemies && Environment.getAttribute("tankattack.sprites.EnemyTank").size()==0) {
					CG.roomFinish = CG.GOOD_FINISH;
					CG.scoreDelay.reset();
				}

				// if basedestroyed
				if( !base.valid() || playersDead  )
				{   CG.roomFinish = CG.BAD_FINISH; }
			}
    }	// end of method.
	
	
} // end of class


class Controls extends KeyAdapter {
	public void keyPressed(KeyEvent e) {
		int ch = e.getKeyCode();
		World curWorld = World.currentWorld;

		// Make arrow keys work in the first room.
		if( curWorld.curRoom instanceof RoomStartUp ) {

			if( ch == KeyEvent.VK_UP && TankAttackWorld.pos>0 ) TankAttackWorld.pos--;
			if( ch == KeyEvent.VK_DOWN && TankAttackWorld.pos<2 ) TankAttackWorld.pos++;
			if( ch == KeyEvent.VK_ENTER ) TankAttackWorld.doChoice();
		}

		if (ch == KeyEvent.VK_PAUSE ) {
		    Game.println("paused button pressed : " + Game.getState() );

			if( Game.getState(Game.PLAY) ) 	    Game.pause();
			else 								Game.resume();
		} 
		// The cheats - 1,2,3,4 for first 4 rooms.  7 kill all enemies
		// Whats required - Be able to kill all enemies | kill all object except player 
		// 
		if( ch == KeyEvent.VK_1 )	curWorld.setNextRoom( "RoomStartUp" );
		if( ch == KeyEvent.VK_2 )	curWorld.setNextRoom( "RoomA" );
		if( ch == KeyEvent.VK_3 )	curWorld.setNextRoom( "RoomB" );
		if( ch == KeyEvent.VK_4 )	curWorld.setNextRoom("RoomGameOver");		

/*		if( ch == KeyEvent.VK_F10 )	{ Game.debugFlag = "rect"; }
		if( ch == KeyEvent.VK_6 )	{ Environment.killSprites( Environment.getAttribute("tankattack.sprites.EnemyTank"), true); Game.pause(); }
		if( ch == KeyEvent.VK_F11 )	{  Game.debugFlag = null; }
//		if( ch == KeyEvent.VK_6 )	Sprite.kill( Environment.getFamily("ENEMY"), false); */
		
//		if( ch == KeyEvent.VK_0 )	Game.setFPS( Game.getFPS()+1 );
//		if( ch == KeyEvent.VK_9 )	Game.setFPS( Game.getFPS()-1 ); // 8,9,0 keys are beign used for showing bounding rect

	}
}

class MyMouseListener extends MouseAdapter implements MouseMotionListener {

	public void mouseClicked(MouseEvent me) {

		if( World.currentWorld != null && World.currentWorld.curRoom instanceof RoomStartUp) {
			Point p = me.getPoint();
			if( TankAttackWorld.r[0].contains(p) || TankAttackWorld.r[1].contains(p) || TankAttackWorld.r[2].contains(p) )
				TankAttackWorld.doChoice();
		}
	}

	public void mouseMoved(MouseEvent me) {
		Point p = me.getPoint();
		if( TankAttackWorld.r[0].contains(p) ) TankAttackWorld.pos = 0;
		if( TankAttackWorld.r[1].contains(p) ) TankAttackWorld.pos = 1;
		if( TankAttackWorld.r[2].contains(p) ) TankAttackWorld.pos = 2;
	}
	public void mouseDragged(MouseEvent me) {}
}